package de.szut.tree;

import java.util.Map.Entry;

/**
 * Node in a binary search tree
 * @author Marc Huisinga
 *
 * @param <K> - Type of the key in this node
 * @param <V> - Type of the value in this node
 */
class Node<K, V> {
	
	private Entry<K, V> entry;
	private int height = 1;
	private int balance = 0;
	private Node<K, V> parent;
	private Node<K, V> leftChild;
	private Node<K, V> rightChild;
	
	/**
	 * Constructor
	 * @param entry - entry stored in this node
	 * @param parent - parent of this node
	 * @param leftChild - left child of this node
	 * @param rightChild - right child of this node
	 */
	public Node(Entry<K, V> entry, Node<K, V> parent, Node<K, V> leftChild, Node<K, V> rightChild) {
		this.entry = entry;
		this.parent = parent;
		this.leftChild = leftChild;
		this.rightChild = rightChild;
	}
	
	/**
	 * Constructor for insertable nodes, has no left- and no right child.
	 * @param entry - entry stored in this node
	 * @param parent - parent of this node
	 */
	public Node(Entry<K, V> entry, Node<K, V> parent) {
		this(entry, parent, null, null);
	}
	
	/**
	 * Constructor for root nodes, has no left- and no right child as well as no parent.
	 * @param entry - entry stored in this node
	 */
	public Node(Entry<K, V> entry) {
		this(entry, null);
	}

	/**
	 * Gets the height of the specified node.
	 * @param node - node to get the height of
	 * @return height of the node or 0 if the node is null
	 */
	private int height(Node<K, V> node) {
		if (node == null) {
			return 0;
		}
		return node.getHeight();
	}
	
	/**
	 * Updates the height of this node by getting the height of its subtrees and incrementing
	 * the larger one of the two.
	 */
	public void updateHeight() {
		int leftHeight = height(leftChild);
		int rightHeight = height(rightChild);
		height = Math.max(leftHeight, rightHeight) + 1;
		balance = leftHeight - rightHeight;
	}
	
	/**
	 * Sets the child that matches the match.
	 * @param match - match predicate
	 * @param node - node to set
	 * @return true if the match is a child of this node and was set or false if not
	 */
	public boolean setMatchingChild(Node<K, V> match, Node<K, V> node) {
		if (leftChild == match) {
			leftChild = node;
			return true;
		}
		if (rightChild == match) {
			rightChild = node;
			return true;
		}
		return false;
	}
	
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		@SuppressWarnings("rawtypes")
		Node other = (Node) obj;
		if (balance != other.balance) {
			return false;
		}
		if (entry == null) {
			if (other.entry != null) {
				return false;
			}
		} else if (!entry.equals(other.entry)) {
			return false;
		}
		if (height != other.height) {
			return false;
		}
		return true;
	}
	
	public Entry<K, V> getEntry() {
		return entry;
	}

	public void setEntry(Entry<K, V> entry) {
		this.entry = entry;
	}
	
	public int getHeight() {
		return height;
	}
	
	public int getBalance() {
		return balance;
	}

	public void setBalance(int balance) {
		this.balance = balance;
	}
	
	public Node<K, V> getParent() {
		return parent;
	}

	public void setParent(Node<K, V> parent) {
		this.parent = parent;
	}

	public Node<K, V> getLeftChild() {
		return leftChild;
	}

	public void setLeftChild(Node<K, V> leftChild) {
		this.leftChild = leftChild;
	}

	public Node<K, V> getRightChild() {
		return rightChild;
	}

	public void setRightChild(Node<K, V> rightChild) {
		this.rightChild = rightChild;
	}
	
}
